package core

import (
	"gark-elastic-job/common"
	"gark-elastic-job/engine"
	"gark-elastic-job/util"
	"github.com/robfig/cron/v3"
	logger "github.com/sirupsen/logrus"
	"time"
)

var garkParser = cron.NewParser(
	cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dom | cron.Descriptor)

type Scheduler struct {
	node          *JobNode
	eventChan     chan engine.JobChangeEvent
	schedulePlans map[string]*engine.SchedulePlan
}

func NewScheduler(node *JobNode) *Scheduler {
	logger.Debug("start Scheduler")
	scheduler := &Scheduler{
		node:          node,
		eventChan:     make(chan engine.JobChangeEvent, 256),
		schedulePlans: make(map[string]*engine.SchedulePlan),
	}
	go scheduler.loopScheduler()
	return scheduler
}

func (s *Scheduler) pushJobChangeCreate(event engine.JobChangeEvent) {
	s.eventChan <- event
}

func (s *Scheduler) loopScheduler() {
	timer := time.NewTimer(time.Second)
	for {
		logger.Debugf("is me running fast??")
		select {
		case <-timer.C:
		case event := <-s.eventChan:
			s.handleJobChangeEvent(event)
		}
		duration := s.tryScheduler()
		timer.Reset(duration)
	}
}

func (s *Scheduler) handleJobChangeEvent(event engine.JobChangeEvent) {
	switch event.Type {
	case engine.JobCreateChangeEvent:
		s.handleJobCreateEvent(event)
	case engine.JobUpdateChangeEvent:
		s.handleJobUpdateEvent(event)
	case engine.JobDeleteChangeEvent:
		s.handleJobDeleteEvent(event)
	}
}

// todo 这里可以优化成一个堆的数据结构进行调度
func (s *Scheduler) tryScheduler() time.Duration {
	if len(s.schedulePlans) == 0 {
		return time.Second
	}

	now := time.Now()
	for _, plan := range s.schedulePlans {
		scheduleTime := plan.NextTime
		if scheduleTime.Before(now) && s.node.State == engine.Leader {
			logger.Infof("schedule execute the plan: %+v", plan)
			snapshot := common.GarkJobSnapshot{
				Id:                     util.GenerateId(),
				Group:                  plan.Group,
				Description:            "",
				Name:                   plan.Name,
				ShardingCount:          1,
				ShardingItemParameters: "",
				Target:                 plan.Target,
				Payload:                plan.Payload,
				Enabled:                true,
				Cron:                   plan.Cron,
				Version:                1,
				Remark:                 plan.Remark,
				CreateTime:             util.Format(now),
			}
			// 执行下次的调度
			plan.Next()
			s.node.Executor.pushJobSnapshot(&snapshot)
		}
	}
	return time.Second
}

func (s *Scheduler) handleJobCreateEvent(event engine.JobChangeEvent) {
	s.createJobPlan(event)
}

func (s *Scheduler) handleJobUpdateEvent(event engine.JobChangeEvent) {

}

func (s *Scheduler) handleJobDeleteEvent(event engine.JobChangeEvent) {
}

func (s *Scheduler) createJobPlan(event engine.JobChangeEvent) {
	jobConf := event.Conf

	if _, ok := s.schedulePlans[jobConf.Id]; ok {
		logger.Warnf("the schedule plan [%s] has already exist", jobConf.Id)
		return
	}

	// todo 是否需要考虑作业状态的设置

	scheduler, err := garkParser.Parse(jobConf.Cron)
	if err != nil {
		logger.Errorf("fail to parse cron spec: %s", jobConf.Cron)
		return
	}
	now := time.Now()
	schedulePlan := &engine.SchedulePlan{
		Id:       jobConf.Id,
		Name:     jobConf.Name,
		Group:    jobConf.Group,
		Cron:     jobConf.Cron,
		Target:   jobConf.Target,
		Payload:  jobConf.Payload,
		Remark:   jobConf.Remark,
		Version:  jobConf.Version,
		Schedule: scheduler,
		NextTime: scheduler.Next(now),
		PrevTime: now,
	}

	s.schedulePlans[jobConf.Id] = schedulePlan
	logger.Infof("success to create a new scheduler plan %v", schedulePlan)

}
